package com.iwuyc.tools.commons.concurrency.impl;

import com.google.common.base.Stopwatch;
import com.iwuyc.tools.commons.concurrency.ConcurrencyService;
import com.iwuyc.tools.commons.concurrency.ConcurrencyTokenService;
import lombok.extern.slf4j.Slf4j;

import java.util.Optional;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;

@Slf4j
public class ConcurrencyServiceImpl implements ConcurrencyService, AutoCloseable {
    private final ExecutorService executorService;
    private final ConcurrencyTokenService concurrencyTokenService;

    public ConcurrencyServiceImpl(ExecutorService executorService, ConcurrencyTokenService concurrencyTokenService) {
        this.executorService = executorService;
        this.concurrencyTokenService = concurrencyTokenService;
    }

    @Override
    public boolean submit(Runnable runnable, long timeout, TimeUnit timeoutUnit) {
        final Optional<ConcurrencyTokenService.ConcurrencyToken> tokenOpt;
        try {
            tokenOpt = this.concurrencyTokenService.take(timeout, timeoutUnit);
            if (!tokenOpt.isPresent()) {
                log.info("获取token失败，timeout:{};timeoutUnit:{}", timeout, timeoutUnit);
                return false;
            }
        } catch (InterruptedException e) {
            log.info("线程被中断，任务结束，原因是：", e);
            Thread.currentThread().interrupt();
            return false;
        }

        // 只有任务执行完，或者出现异常的时候，才可以close掉token
        final ConcurrencyTokenService.ConcurrencyToken token = tokenOpt.get();
        try {
            this.executorService.submit(new ContextRunnable(runnable, token));
            return true;
        } catch (Exception e) {
            token.close();
            log.error("提交任务失败，原因是：", e);
            return false;
        }
    }
    @Override
    public boolean concurrency(int concurrency) {
        return this.concurrencyTokenService.concurrency(concurrency);
    }

    @Override
    public void close() {
        if (null != executorService) {
            executorService.shutdown();
        }
    }

    private static final class ContextRunnable implements Runnable {
        private final Runnable proxy;
        private final ConcurrencyTokenService.ConcurrencyToken token;
        private final Stopwatch started;

        private ContextRunnable(Runnable proxy, ConcurrencyTokenService.ConcurrencyToken token) {
            this.proxy = proxy;
            this.token = token;
            this.started = Stopwatch.createStarted();
        }

        @Override
        public void run() {
            try {
                log.info("开始执行实际业务。耗时：{}", started.elapsed());
                this.proxy.run();
                log.info("已经执行完实际业务。耗时：{}", started.stop());
            } catch (Exception e) {
                log.error("执行业务逻辑出现异常，原因是：", e);
            } finally {
                this.token.close();
            }
        }
    }

}